﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace ACLibs.WinAPI
{
    public partial class User32
    {
        public const string LibraryName = "user32.dll";

        //常量(枚举)在文件User32.Constants.cs

        #region 结构:struct
        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;

            public RECT(int left, int top, int right, int bottom)
            {
                Left = left;
                Top = top;
                Right = right;
                Bottom = bottom;
            }

            public RECT(Rectangle rect)
            {
                Left = rect.Left;
                Top = rect.Top;
                Right = rect.Right;
                Bottom = rect.Bottom;
            }

            public Rectangle Rect
            {
                get
                {
                    return new Rectangle(
                        Left,
                        Top,
                        Right - Left,
                        Bottom - Top);
                }
            }

            public Size Size
            {
                get
                {
                    return new Size(Right - Left, Bottom - Top);
                }
            }

            public static RECT FromXYWH(int x, int y, int width, int height)
            {
                return new RECT(x,
                                y,
                                x + width,
                                y + height);
            }

            public static RECT FromRectangle(Rectangle rect)
            {
                return new RECT(rect.Left,
                                 rect.Top,
                                 rect.Right,
                                 rect.Bottom);
            }
        }
        #endregion

        #region 回调函数
        /// <summary>
        /// 回调函数-与EnumWindows或EnumDesktopWindows函数一起使用的应用程序定义的回调函数。它接收顶级窗口句柄。
        /// WNDENUMPROC类型定义一个指向此回调函数的指针。
        /// EnumWindowsProc是应用程序定义的函数名称的占位符。
        /// </summary>
        /// <param name="hWnd">顶层窗口的句柄。</param>
        /// <param name="lParam">在EnumWindows或EnumDesktopWindows中给出的应用程序定义的值。</param>
        /// <returns></returns>
        public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

        #endregion

        /// <summary>
        /// 通过将句柄传递给每个窗口，依次传递给应用程序定义的回调函数，可以枚举屏幕上所有的顶级窗口。
        /// EnumWindows继续，直到枚举最后一个顶级窗口或回调函数返回FALSE。
        /// </summary>
        /// <param name="lpEnumFunc">指向应用程序定义的回调函数的指针。</param>
        /// <param name="lParam">应用程序定义的值，将传递给回调函数。</param>
        /// <returns></returns>
        [DllImport(LibraryName, ExactSpelling = true)]
        public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);

        /// <summary>
        /// 枚举与指定桌面关联的所有顶级窗口。它将句柄传递给每个窗口，依次传递给应用程序定义的回调函数。
        /// </summary>
        /// <param name="hDesktop">要枚举其顶级窗口的桌面的句柄。该句柄由CreateDesktop，GetThreadDesktop，OpenDesktop或OpenInputDesktop函数返回，并且必须具有DESKTOP_READOBJECTS访问权限。有关更多信息，请参见桌面安全和访问权限。如果此参数为NULL，则使用当前桌面。</param>
        /// <param name="lpEnumFunc">指向应用程序定义的EnumWindowsProc回调函数的指针。</param>
        /// <param name="lParam">应用程序定义的值，将传递给回调函数。</param>
        /// <returns></returns>
        [DllImport(LibraryName, ExactSpelling = true)]
        public static extern bool EnumDesktopWindows(IntPtr hDesktop, EnumWindowsProc lpEnumFunc, IntPtr lParam);

        /// <summary>
        /// 将指定窗口标题栏的文本（如果有的话）复制到缓冲区中。
        /// 如果指定的窗口是控件，则复制控件的文本。
        /// 但是，GetWindowText无法在另一个应用程序中检索控件的文本。
        /// </summary>
        /// <param name="hWnd">包含文本的窗口或控件的句柄</param>
        /// <param name="lpString">将接收文本的缓冲区。如果字符串与缓冲区一样长或更长，则字符串将被截断并以空字符终止。</param>
        /// <param name="nMaxCount">要复制到缓冲区的最大字符数，包括空字符。如果文本超过此限制，则会被截断。</param>
        /// <returns></returns>
        [DllImport(LibraryName, CharSet = Properties.BuildCharSet)]
        public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

        /// <summary>
        /// 检索指定窗口的标题栏文本的长度（以字符为单位）（如果窗口具有标题栏）。
        /// 如果指定的窗口是控件，则该函数将检索控件内文本的长度。
        /// 但是，GetWindowTextLength无法在另一个应用程序中检索编辑控件的文本长度。
        /// </summary>
        /// <param name="hWnd">窗口或控件的句柄。</param>
        /// <returns></returns>
        [DllImport(LibraryName, CharSet = Properties.BuildCharSet)]
        public static extern int GetWindowTextLength(IntPtr hWnd);

        /// <summary>
        /// 确定指定窗口的可见性状态。
        /// </summary>
        /// <param name="hwnd">要测试的窗口的句柄。</param>
        /// <returns></returns>
        [DllImport(LibraryName, ExactSpelling = true)]
        public static extern bool IsWindowVisible(IntPtr hwnd);

        /// <summary>
        /// 检索创建指定窗口的线程的标识符，以及可选的创建窗口的进程的标识符。
        /// </summary>
        /// <param name="hWnd">窗口的句柄。</param>
        /// <param name="processId">
        /// 指向接收进程标识符的变量的指针。
        /// 如果此参数不为NULL，则GetWindowThreadProcessId将进程的标识符复制到该变量；否则，不会复制。
        /// </param>
        /// <returns></returns>
        [DllImport(LibraryName, ExactSpelling = true)]
        public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId);

        /// <summary>
        /// 检索顶级窗口的句柄，该窗口的类名和窗口名与指定的字符串匹配。此功能不搜索子窗口。
        /// 此功能不执行区分大小写的搜索。要从指定的子窗口开始搜索子窗口，请使用FindWindowEx函数。
        /// </summary>
        /// <param name="lpClassName">
        /// [Type: LPCTSTR]
        /// 通过先前调用RegisterClass或RegisterClassEx函数创建的类名称或类原子。
        /// 原子必须在lpClassName的低位单词中；高阶字必须为零。如果lpClassName指向一个字符串，则它指定窗口类名称。
        /// 类名可以是在RegisterClass或RegisterClassEx中注册的任何名称，也可以是任何预定义的控件类名称。
        /// 如果lpClassName为NULL，它将找到标题与lpWindowName参数匹配的任何窗口。
        /// </param>
        /// <param name="lpWindowName">
        /// [Type: LPCTSTR]
        /// 窗口名称（窗口标题）。如果此参数为NULL，则所有窗口名称均匹配。
        /// </param>
        /// <returns></returns>
        [DllImport(LibraryName, CharSet = Properties.BuildCharSet)]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        /// <summary>
        /// 检索其类名和窗口名与指定字符串匹配的窗口的句柄。该功能搜索子窗口，从指定子窗口之后的子窗口开始。此功能不执行区分大小写的搜索。
        /// </summary>
        /// <param name="hwndParent">
        /// [Type: HWND]
        /// 要搜索其子窗口的父窗口的句柄。如果hwndParent为NULL，则该函数将桌面窗口用作父窗口。
        /// 该功能在作为桌面子窗口的窗口之间搜索。如果hwndParent为HWND_MESSAGE，则该函数搜索所有仅消息窗口。
        /// </param>
        /// <param name="hwndChildAfter">
        /// [Type: HWND]
        /// 子窗口的句柄。搜索从Z顺序的下一个子窗口开始。子窗口必须是hwndParent的直接子窗口，而不仅仅是后代窗口。
        /// 如果hwndChildAfter为NULL，则搜索从hwndParent的第一个子窗口开始。
        /// 请注意，如果hwndParent和hwndChildAfter均为NULL，则该函数将搜索所有顶级和仅消息窗口。
        /// </param>
        /// <param name="lpszClass">
        /// [Type: LPCTSTR]
        /// 通过先前调用RegisterClass或RegisterClassEx函数创建的类名称或类原子。
        /// 原子必须放在lpszClass的低位字中；高阶字必须为零。如果lpszClass是一个字符串，则它指定窗口类名称。
        /// 类名可以是在RegisterClass或RegisterClassEx中注册的任何名称，也可以是任何预定义的控件类名称，也可以是MAKEINTATOM（0x8000）。
        /// 在后一种情况下，0x8000是菜单类的原子。
        /// </param>
        /// <param name="lpszWindow">
        /// [Type: LPCTSTR]
        /// 窗口名称（窗口标题）。如果此参数为NULL，则所有窗口名称均匹配。
        /// </param>
        /// <returns>[Type: HWND]</returns>
        [DllImport(LibraryName, CharSet = Properties.BuildCharSet)]
        public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

        /// <summary>
        /// 设置指定窗口的显示状态。
        /// </summary>
        /// <param name="hwnd">窗口的句柄</param>
        /// <param name="nCmdShow">
        /// [Type: int]
        /// 控制窗口的显示方式。
        /// 如果启动应用程序的程序提供了STARTUPINFO结构，则在应用程序首次调用ShowWindow时将忽略此参数。
        /// 否则，第一次调用ShowWindow时，该值应为WinMain函数在其nCmdShow参数中获得的值。
        /// </param>
        /// <returns></returns>
        [DllImport(LibraryName, ExactSpelling = true)]
        public static extern bool ShowWindow(IntPtr hwnd, ShowWindowCommands nCmdShow);

        /// <summary>
        /// 检索指定窗口的边界矩形的尺寸。尺寸以相对于屏幕左上角的屏幕坐标给出。
        /// </summary>
        /// <param name="hwnd">[Type:HWND] 窗口的句柄。</param>
        /// <param name="lpRect">[Type：LPRECT] 指向RECT结构的指针，该结构接收窗口左上角和右下角的屏幕坐标。</param>
        /// <returns>如果函数成功，则返回值为非零。如果函数失败，则返回值为零。若要获取扩展的错误信息，请调用GetLastError。</returns>
        [DllImport(LibraryName, ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GetWindowRect(IntPtr hwnd, ref RECT lpRect);

        // 此静态方法是必需的，因为Win32不直接支持GetWindowLongPtr
        /// <summary>
        /// 检索有关指定窗口的信息。该函数还将指定偏移量的值检索到额外的窗口存储器中。
        /// 注意要编写与32位和64位版本的Windows兼容的代码，请使用GetWindowLongPtr。
        /// 当为32位Windows进行编译时，GetWindowLongPtr被定义为对GetWindowLong函数的调用。
        /// </summary>
        /// <param name="hwnd">[Type:HWND]窗口的句柄，间接地，窗口所属的类。</param>
        /// <param name="nIndex">
        /// [Type:int] 
        /// 从零开始的要获取值的偏移量。有效值的范围是零到额外的窗口内存的字节数减去LONG_PTR的大小。要检索其他任何值，请指定以下值之一。
        /// 应为枚举WindowLongFlags的值
        /// </param>
        /// <returns>[Type:LONG_PTR] 如果函数成功，则返回值为请求的值。</returns>
        public static IntPtr GetWindowLongPtr(IntPtr hwnd, int nIndex)
        {
            return IntPtr.Size > 4
                ? GetWindowLongPtr_x64(hwnd, nIndex)
                : new IntPtr(GetWindowLong(hwnd, nIndex));
        }

        [DllImport(LibraryName, CharSet = Properties.BuildCharSet)]
        private static extern int GetWindowLong(IntPtr hwnd, int nIndex);

        [DllImport(LibraryName, CharSet = Properties.BuildCharSet, EntryPoint = "GetWindowLongPtr")]
        private static extern IntPtr GetWindowLongPtr_x64(IntPtr hwnd, int nIndex);

        // 此静态方法是必需的，因为Win32不直接支持GetWindowLongPtr
        /// <summary>
        /// 更改指定窗口的属性。该函数还在附加窗口存储器中的指定偏移处设置一个值。
        /// 注意要编写与32位和64位版本的Windows兼容的代码，请使用SetWindowLongPtr。
        /// 当为32位Windows进行编译时，SetWindowLongPtr被定义为对SetWindowLong函数的调用。
        /// </summary>
        /// <param name="hwnd">
        /// [Type:HWND]
        /// 窗口的句柄，间接地，窗口所属的类。
        /// 如果拥有hWnd参数指定的窗口的进程在UIPI层次结构中具有比调用线程所驻留的进程更高的进程特权，则SetWindowLongPtr函数将失败。
        /// </param>
        /// <param name="nIndex">
        /// [Type:int] 
        /// 从零开始的要设置值的偏移量。有效值的范围是零到额外的窗口内存的字节数减去LONG_PTR的大小。要设置任何其他值，请指定以下值之一。
        /// 应为枚举WindowLongFlags的值
        /// </param>
        /// <param name="dwNewLong">替换值。</param>
        /// <returns>[Type:LONG_PTR] 如果函数成功，则返回值是指定偏移量的先前值。</returns>
        public static IntPtr SetWindowLongPtr(IntPtr hwnd, int nIndex, IntPtr dwNewLong)
        {
            return IntPtr.Size > 4
                ? SetWindowLongPtr_x64(hwnd, nIndex, dwNewLong)
                : new IntPtr(SetWindowLong(hwnd, nIndex, dwNewLong.ToInt32()));
        }

        [DllImport(LibraryName, CharSet = Properties.BuildCharSet)]
        private static extern int SetWindowLong(IntPtr hwnd, int nIndex, int dwNewLong);

        [DllImport(LibraryName, CharSet = Properties.BuildCharSet, EntryPoint = "SetWindowLongPtr")]
        private static extern IntPtr SetWindowLongPtr_x64(IntPtr hwnd, int nIndex, IntPtr dwNewLong);

        /// <summary>
        /// 更改子窗口，弹出窗口或顶级窗口的大小，位置和Z顺序。这些窗口是根据其在屏幕上的外观排序的。
        /// 最顶部的窗口获得最高排名，并且是Z顺序中的第一个窗口。
        /// </summary>
        /// <param name="hwnd" type=HWND>窗口的句柄。</param>
        /// <param name="hWndInsertAfter" type=HWND>
        /// 在Z顺序中位于定位的窗口之前的窗口的句柄。此参数必须是窗口句柄或以下值之一。
        /// 应为枚举 HwndZOrder 的值
        /// </param>
        /// <param name="x" type=int>窗口左侧的新位置，以客户坐标表示。</param>
        /// <param name="y" type=int>窗口顶部的新位置，以客户坐标表示。</param>
        /// <param name="cx" type=int>窗口的新宽度，以像素为单位。</param>
        /// <param name="cy" type=int>窗口的新高度，以像素为单位。</param>
        /// <param name="flags" type=uint>
        /// 窗口大小和位置标志。此参数可以是以下值的组合。
        /// 应为枚举 WindowPositionFlags 的值
        /// </param>
        /// <returns></returns>
        [DllImport(LibraryName, ExactSpelling = true)]
        public static extern bool SetWindowPos(IntPtr hwnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, WindowPositionFlags flags);

        [DllImport(LibraryName, CharSet = Properties.BuildCharSet)]
        public static extern IntPtr SendMessage(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dwFlags">控制鼠标移动和按钮单击的各个方面。该参数可以是以下值的某些组合。[枚举：MouseInputFlags]</param>
        /// <param name="dx">自上次鼠标事件发生以来，鼠标沿x轴的绝对位置或其运动量，取决于MOUSEEVENTF_ABSOLUTE的设置。
        ///                  绝对数据被指定为鼠标的实际x坐标；</param>
        /// <param name="dy">自上次鼠标事件发生以来，鼠标沿y轴的绝对位置或其运动量，取决于MOUSEEVENTF_ABSOLUTE的设置。
        ///                  绝对数据指定为鼠标的实际y坐标；相对数据被指定为移动的米键数。</param>
        /// <param name="dwData">[根据说明,选择枚举MouseInputXButtonFlag,或数值,或0]
        ///                      如果dwFlags包含MOUSEEVENTF_WHEEL，则dwData指定车轮移动量。
        ///                      正值表示轮子向前旋转，远离用户；负值表示方向盘朝着用户向后旋转。
        ///                      一轮点击定义为WHEEL_DELTA，即120。如果dwFlags包含MOUSEEVENTF_HWHEEL，则dwData指定车轮移动量。
        ///                      正值表示车轮向右倾斜；负值表示车轮向左倾斜。
        ///                      如果dwFlags包含MOUSEEVENTF_XDOWN或MOUSEEVENTF_XUP，则dwData指定按下或释放了X个按钮。该值可以是以下标志的任意组合。
        ///                      如果dwFlags不是MOUSEEVENTF_WHEEL，MOUSEEVENTF_XDOWN或MOUSEEVENTF_XUP，则dwData应该为零。</param>
        /// <param name="dwExtraInfo">与鼠标事件关联的附加值。应用程序调用GetMessageExtraInfo以获得此额外信息。</param>
        [DllImport(LibraryName, CharSet = Properties.BuildCharSet)]
        public static extern void Mouse_Event(MouseInputFlags dwFlags, int dx, int dy, int dwData, IntPtr dwExtraInfo);
    }
}
